package com.jsftoolkit.ajax;

import static javax.faces.event.PhaseId.ANY_PHASE;
import static javax.faces.event.PhaseId.INVOKE_APPLICATION;
import static javax.faces.event.PhaseId.RESTORE_VIEW;

import javax.faces.FacesException;
import javax.faces.context.FacesContext;
import javax.faces.event.PhaseEvent;
import javax.faces.event.PhaseId;
import javax.faces.event.PhaseListener;

import com.jsftoolkit.utils.Utils;

/**
 * This class is responsible for loading the {@link AjaxContextFactory} and
 * using it to create the {@link AjaxContext} for each request.
 * <p>
 * The class that is loaded can be specified in web.xml via the
 * {@value #FACTORY_CLASS} parameter.
 * 
 * @author noah
 * 
 */
public class AjaxContextLoader implements PhaseListener {

	public static final String FACTORY_CLASS = "com.jsftoolkit.ajax.AjaxContextLoader.FACTORY_CLASS";

	private static final long serialVersionUID = 1L;

	private AjaxContextFactory factory;

	/**
	 * Clears the {@link AjaxContext} after {@link PhaseId#INVOKE_APPLICATION}.
	 */
	public void afterPhase(PhaseEvent event) {
		Utils.notNull(event, "event");
		if (INVOKE_APPLICATION == event.getPhaseId()) {
			AjaxContext.setCurrentInstance(null);
		}
	}

	/**
	 * Sets up the {@link AjaxContext} before {@link PhaseId#RESTORE_VIEW}.
	 */
	public void beforePhase(PhaseEvent event) {
		Utils.notNull(event, "event");
		if (RESTORE_VIEW == event.getPhaseId()
				&& AjaxContext.getCurrentInstance() == null) {
			FacesContext context = event.getFacesContext();
			AjaxContextFactory factory = getFactory(context);
			AjaxContext.setCurrentInstance(factory.create(context));
		}
	}

	protected AjaxContextFactory getFactory(FacesContext context) {
		String className = context.getExternalContext().getInitParameter(
				FACTORY_CLASS);
		if (factory == null) {
			// cache the created factory for the life of this phase listener.
			if (className == null) {
				factory = new DefaultAjaxContextFactory();
			} else {
				try {
					factory = (AjaxContextFactory) Class.forName(className)
							.newInstance();
				} catch (Exception e) {
					throw new FacesException(
							"Error instantiating AjaxContextFactory", e);
				}
			}
		}
		return factory;
	}

	/**
	 * @return {@link PhaseId#ANY_PHASE}, but only acts after
	 *         {@link PhaseId#INVOKE_APPLICATION} and before
	 *         {@link PhaseId#RESTORE_VIEW}.
	 */
	public PhaseId getPhaseId() {
		return ANY_PHASE;
	}

	public AjaxContextFactory getFactory() {
		return factory;
	}

	public void setFactory(AjaxContextFactory factory) {
		this.factory = factory;
	}

}
